package com.gee.spring.cloud.feign.flow.config;

import com.gee.spring.cloud.feign.flow.binding.FeignFlowFactoryBean;
import com.gee.spring.cloud.feign.flow.binding.annotation.EnableFeignFlows;
import com.gee.spring.cloud.feign.flow.binding.annotation.FeignFlowClient;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/**
 * desc:
 *
 * @author gee
 * @since 2021-04-02 10:31:36
 */
public class FeignFlowRegister implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware, BeanFactoryAware {

    private ResourceLoader resourceLoader;
    private Environment environment;
    private BeanFactory beanFactory;

    public ResourceLoader getResourceLoader() {
        return resourceLoader;
    }

    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    public Environment getEnvironment() {
        return environment;
    }

    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        registerFeignClients(importingClassMetadata,registry);
    }

    public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        ClassPathScanningCandidateComponentProvider scanner = this.getScanner();
        scanner.setResourceLoader(this.resourceLoader);
        AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(FeignFlowClient.class);
        scanner.addIncludeFilter(annotationTypeFilter);
        Set<String> basePackages = this.getBasePackages(metadata);
        Iterator var17 = basePackages.iterator();
        while(var17.hasNext()) {
            String basePackage = (String)var17.next();
            Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(basePackage);
            for (BeanDefinition candidateComponent : candidateComponents) {
                if (candidateComponent instanceof AnnotatedBeanDefinition) {
                    AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition)candidateComponent;
                    this.registerFeignClient(registry, beanDefinition);
                }
            }
        }
    }

    private void registerFeignClient(BeanDefinitionRegistry registry, AnnotatedBeanDefinition annotatedBeanDefinition) {
        AnnotationMetadata annotationMetadata = annotatedBeanDefinition.getMetadata();
        Assert.isTrue(annotationMetadata.isInterface(), "@FeignClientFlow can only be specified on an interface");

        BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(FeignFlowFactoryBean.class);
        definition.addPropertyReference("feignFlowThreadPoolExecutor", "feignFlowThreadPoolExecutor");

        AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
        beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(annotationMetadata.getClassName());
        beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(this.beanFactory);

        BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, annotationMetadata.getClassName());
        BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
    }

    private String getQualifier(Map<String, Object> client) {
        if (client == null) {
            return null;
        } else {
            String qualifier = (String)client.get("qualifier");
            return StringUtils.hasText(qualifier) ? qualifier : null;
        }
    }

    protected ClassPathScanningCandidateComponentProvider getScanner() {
        return new ClassPathScanningCandidateComponentProvider(false, this.environment) {
            protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
                boolean isCandidate = false;
                if (beanDefinition.getMetadata().isIndependent() && !beanDefinition.getMetadata().isAnnotation()) {
                    isCandidate = true;
                }

                return isCandidate;
            }
        };
    }

    protected Set<String> getBasePackages(AnnotationMetadata importingClassMetadata) {
        Map<String, Object> attributes = importingClassMetadata.getAnnotationAttributes(EnableFeignFlows.class.getCanonicalName());
        Set<String> basePackages = new HashSet();
        String[] var4 = (String[])attributes.get("value");
        String pkg;
        for(int i = 0; i < var4.length; ++i) {
            pkg = var4[i];
            if (StringUtils.hasText(pkg)) {
                basePackages.add(pkg);
            }
        }
        if (basePackages.isEmpty()) {
            basePackages.add(ClassUtils.getPackageName(importingClassMetadata.getClassName()));
        }
        return basePackages;
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }
}
